package com.leyou.item.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.common.dto.PageDTO;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.dto.SkuDTO;
import com.leyou.item.dto.SpecParamDTO;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.dto.SpuDetailDTO;
import com.leyou.item.entity.*;
import com.leyou.item.mapper.SpuMapper;
import com.leyou.item.service.*;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.leyou.common.constants.MQConstants.ExchangeConstants.ITEM_EXCHANGE_NAME;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.ITEM_DOWN_KEY;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.ITEM_UP_KEY;

/**
 * spu表，该表描述的是一个抽象性的商品，比如 iphone8(Spu)表服务实现类
 *
 * @author makejava
 * @since 2020-09-15 16:37:20
 */
@Service
public class SpuServiceImpl extends ServiceImpl<SpuMapper, Spu> implements SpuService {

    private final BrandService brandService;
    private final CategoryService categoryService;
    private final SpuDetailService detailService;
    private final SkuService skuService;
    private final SpecParamService specParamService;
    private final AmqpTemplate amqpTemplate;

    public SpuServiceImpl(BrandService brandService, CategoryService categoryService, SpuDetailService detailService, SkuService skuService, SpecParamService specParamService, AmqpTemplate amqpTemplate) {
        this.brandService = brandService;
        this.categoryService = categoryService;
        this.detailService = detailService;
        this.skuService = skuService;
        this.specParamService = specParamService;
        this.amqpTemplate = amqpTemplate;
    }

    @Override
    public PageDTO<SpuDTO> querySpuByPage(Long brandId, Long categoryId, Long id, Integer page, Integer rows, Boolean saleable) {
        // 健壮性处理
        if (page < 1 || rows < 1) {
            throw new LyException(400, "分页参数有误！");
        }

        //1.查询 SELECT * FROM tb_spu
        Page<Spu> result = query()
                //WHERE id = 1 AND cid3 = 1 AND brand_id = 1 AND saleable = true
                .eq(id != null, "id", id)
                .eq(brandId != null, "brand_id", brandId)
                .eq(categoryId != null, "cid3", categoryId)
                .eq(saleable != null, "saleable", saleable)
                //LIMIT 0, 5
                .page(new Page<>(page, rows));

        // 2.转换DTO
        List<Spu> list = result.getRecords();
        List<SpuDTO> dtoList = SpuDTO.convertEntityList(list);
        // 3.处理分类和品牌名称
        for (SpuDTO spuDTO : dtoList) {
            handleCategoryAndBrandName(spuDTO);
        }
        // 4.封装PageDTO
        PageDTO<SpuDTO> pageDTO = new PageDTO<>();
        pageDTO.setTotal(result.getTotal());
        pageDTO.setTotalPage(result.getPages());
        pageDTO.setItems(dtoList);

        // 5.返回
        return pageDTO;
    }

    @Override
    @Transactional
    public void saveGoods(SpuDTO spuDTO) {
        // 1.新增SPU
        // 1.1.把DTO转为PO
        Spu spu = spuDTO.toEntity(Spu.class);
        // 1.2.填充默认字段，默认下架
        spu.setSaleable(false);
        // 1.3.新增
        boolean success = save(spu);
        if (!success) {
            // 新增失败
            throw new LyException(500, "新增商品SPU失败！");
        }
        // 1.4.获取回显的id
        Long spuId = spu.getId();

        // 2.新增SpuDetail
        // 2.1.获取detail的DTO
        SpuDetailDTO spuDetailDTO = spuDTO.getSpuDetail();
        // 2.2.把DTO转为PO
        SpuDetail spuDetail = spuDetailDTO.toEntity(SpuDetail.class);
        // 2.3.填写主键
        spuDetail.setSpuId(spuId);
        // 2.4.新增
        success = detailService.save(spuDetail);
        if (!success) {
            // 新增失败
            throw new LyException(500, "新增商品SpuDetail失败！");
        }

        // 3.新增Sku
        // 3.1.获取SkuDTO的集合
        List<SkuDTO> skuDTOList = spuDTO.getSkus();
        // 3.2.把DTO转为PO
        List<Sku> skuList = new ArrayList<>(skuDTOList.size());
        for (SkuDTO skuDTO : skuDTOList) {
            Sku sku = skuDTO.toEntity(Sku.class);
            // 3.3.处理默认字段值
            sku.setSpuId(spuId);
            sku.setSold(0L);
            sku.setSaleable(false);
            skuList.add(sku);
        }
        // 3.3.写入数据库
        skuService.saveBatch(skuList);
    }

    @Override
    @Transactional
    public void updateSaleable(Long spuId, Boolean saleable) {
        // 更新spu
        Spu spu = new Spu();
        spu.setId(spuId);
        spu.setSaleable(saleable);
        boolean success = updateById(spu);
        if (!success) {
            throw new LyException(500, "上下架失败！");
        }

        // 更新sku update tb_sku set saleable = #{saleable} where spu_id = #{spuId}
        success = skuService.update().set("saleable", saleable).eq("spu_id", spuId).update();
        if (!success) {
            throw new LyException(500, "上下架失败！");
        }

        // 发消息，发送spuId
        String routingKey = saleable ? ITEM_UP_KEY : ITEM_DOWN_KEY;
        amqpTemplate.convertAndSend(ITEM_EXCHANGE_NAME, routingKey, spuId);
    }

    @Override
    public SpuDTO queryGoodsById(Long spuId) {
        // 1.根据spuId查询spu
        // 1.1.查询
        Spu spu = getById(spuId);
        // 1.2.转换DTO
        SpuDTO spuDTO = new SpuDTO(spu);

        // 2.根据spuId查询spuDetail
        spuDTO.setSpuDetail(detailService.queryDetailBySpuId(spuId));

        // 3.根据spuId查询sku的集合
        spuDTO.setSkus(skuService.querySkuBySpuId(spuId));

        // 4.处理名称
        handleCategoryAndBrandName(spuDTO);
        return spuDTO;
    }

    @Override
    @Transactional
    public void updateGoods(SpuDTO spuDTO) {
        // 1.更新spu
        // 1.1.获取spuId
        Long spuId = spuDTO.getId();
        // 1.2.判断是否存在
        if (spuId != null) {
            // 1.3.存在，spu需要被修改，转换为PO
            Spu spu = spuDTO.toEntity(Spu.class);
            // 1.4.避免修改上下架字段
            spu.setSaleable(null);
            // 1.5.修改
            boolean success = updateById(spu);
            if (!success) {
                throw new LyException(500, "更新Spu失败");
            }
        }

        // 2.更新spuDetail
        // 2.1.获取detail的DTO
        SpuDetailDTO spuDetailDTO = spuDTO.getSpuDetail();
        // 2.2.判断是否存在
        if (spuDetailDTO != null && spuDetailDTO.getSpuId() != null) {
            // 2.3.存在，需要修改，转换PO
            SpuDetail spuDetail = spuDetailDTO.toEntity(SpuDetail.class);
            // 2.4.更新
            boolean success = detailService.updateById(spuDetail);
            if (!success) {
                throw new LyException(500, "更新Spu失败");
            }
        }

        // 3.更新sku
        // 3.1.获取skuDTO
        List<SkuDTO> skuDTOList = spuDTO.getSkus();
        // 3.2.判断是否存在
        if (CollectionUtils.isEmpty(skuDTOList)) {
            // 不存在，结束
            return;
        }
        // 3.3.存在，则需要增删改，把DTO转为PO集合
        Map<Boolean, List<Sku>> skuMap = skuDTOList // List<SkuDTO>
                .stream() // Stream<SkuDTO>
                .map(skuDTO -> skuDTO.toEntity(Sku.class)) // Stream<Sku>
                // 3.4.把sku分组，包含saleable的一组，不包含的放另一组
                .collect(Collectors.groupingBy(sku -> sku.getSaleable() != null));
        // 3.5.取出包含saleable的，这些需要被删除
        List<Sku> deleteSkuList = skuMap.get(true);
        if (!CollectionUtils.isEmpty(deleteSkuList)) {
            // 有需要删除的，开始处理，获取这些sku的id
            List<Long> idList = deleteSkuList.stream().map(Sku::getId).collect(Collectors.toList());
            boolean success = skuService.removeByIds(idList);
            if (!success) {
                throw new LyException(500, "删除Sku失败");
            }
        }
        // 3.6.取出不包含saleable的，这些需要新增或修改
        List<Sku> saveOrUpdateSkuList = skuMap.get(false);
        if (CollectionUtils.isEmpty(saveOrUpdateSkuList)) {
            return;
        }
        // 需要增或改
        skuService.saveOrUpdateBatch(saveOrUpdateSkuList);
    }

    @Override
    public List<SpecParamDTO> querySpecValue(Long spuId, Boolean searching) {
        // 1.根据商品id，查询Spu
        Spu spu = getById(spuId);
        if (spu == null) {
            throw new LyException(400, "商品不存在！");
        }
        //2.从Spu中获取商品分类id
        Long categoryId = spu.getCid3();

        //3.根据商品分类id和searching，查询规格参数
        List<SpecParamDTO> params = specParamService.queryParams(categoryId, null, searching);

        //4.根据商品id，查询spuDetail，从中获取规格参数值
        SpuDetail spuDetail = detailService.getById(spuId);
        if (spuDetail == null) {
            throw new LyException(400, "商品不存在！");
        }
        // 4.1.取出规格参数的json值
        String json = spuDetail.getSpecification();
        // 4.2.把json反序列化为一个Map
        Map<Long, Object> specification = JsonUtils.toMap(json, Long.class, Object.class);

        //5.根据参数id，寻找对应规格参数的值，存入value
        for (SpecParamDTO param : params) {
            // 获取参数id
            Long paramId = param.getId();
            // 从map中根据paramId查询出对应的值
            Object value = specification.get(paramId);
            // 赋值
            param.setValue(value);
        }
        return params;
    }

    private void handleCategoryAndBrandName(SpuDTO spuDTO) {
        // 1.处理品牌
        // 1.1.获取品牌id
        Long brandId = spuDTO.getBrandId();
        // 1.2.查询品牌
        Brand brand = brandService.getById(brandId);
        if (brand == null) {
            // 商品相关的品牌不存在，数据有误
            throw new LyException(400, "商品相关品牌不存在！");
        }
        // 1.3.赋值
        spuDTO.setBrandName(brand.getName());

        // 2.处理分类
        // 2.1.获取分类id
        List<Long> categoryIds = spuDTO.getCategoryIds();
        // 2.2.查询分类
        List<Category> categories = categoryService.listByIds(categoryIds);
        // 2.3.健壮性判断
        if (CollectionUtils.isEmpty(categories)) {
            // 商品相关的分类不存在，数据有误
            throw new LyException(400, "商品相关分类不存在！");
        }
        // 2.4.拼接多个分类的名称
        String names = categories.stream().map(Category::getName).collect(Collectors.joining("/"));
        spuDTO.setCategoryName(names);

        /*
        StringBuilder sb = new StringBuilder();
        for (Category category : categories) {
            sb.append(category.getName()).append("/");
        }
        sb.deleteCharAt(sb.length() - 1);
        spuDTO.setCategoryName(sb.toString());
        */
    }
}